package com.aidansmeaton.weightmanager;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;
import java.util.TreeMap;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import android.app.IntentService;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.os.IBinder;
import android.util.Log;

import com.aidansmeaton.weightmanager.data.DatabaseHelper;

public class ImageRecognitionService extends IntentService {
	
	private String key = "5d2f43e7209c407fb910b074e127b1ce";
	private String secret = "87d0d1e237c145338ed13fcca65d67ed";
	
	private DatabaseHelper db;

	public ImageRecognitionService() {
		super("ImageRecognitionService");
	}
	
	public ImageRecognitionService(String name) {
		super(name);
	}
	
   @Override
   public IBinder onBind(Intent intent) {
	   super.onBind(intent);
      return null;
   }

   @Override
   public void onCreate() {
	   super.onCreate();
	   db = DatabaseHelper.getInstance(this);
	   //code to execute when the service is first created
   }

   @Override
   public void onDestroy() {
	   super.onDestroy();
	   //code to execute when the service is shutting down
   }
   
   @Override
	protected void onHandleIntent(Intent intent) {
	   
	   // Decode image
	   Log.w("HI","HI");
	   String photoPath = intent.getExtras().getString("photoPath");
	   String fullPhotoPath = getFilesDir().getAbsolutePath()+"/"+photoPath;
	   Bitmap image = BitmapFactory.decodeFile(fullPhotoPath);
	   
	   // Upload to server
	   uploadImage(image);
	   
	   // Query IQ Engines
	   String result = query();
	   
	   // Find food records for the results
	   Cursor c = db.getFoodLogsWithPhoto(photoPath);
	   if (c.getCount() == 0) return;
	   c.moveToFirst();
	   while (!c.isAfterLast()){
		   int id = c.getInt(DatabaseHelper.FOOD_LOG_ID);
		   String date = c.getString(DatabaseHelper.FOOD_LOG_DATE);
		   String name = c.getString(DatabaseHelper.FOOD_LOG_NAME);
		   db.editFoodLog(id, date, name, result, photoPath);
		   c.moveToNext();
	   }
	   
	}

@Override
   public int onStartCommand(Intent intent, int flags, int startid) {
	   Log.w("SC","SC");
	   return super.onStartCommand(intent, flags, startid);
   }  
   
   
   private String query(){
	   float progress = 40;
		String qid = "";
		String description = "";
		
		try {				
			// Create a new HttpClient and Post Header
			HttpClient httpclient = new DefaultHttpClient();
			HttpPost httppost = new HttpPost("http://www.weightmanager.host22.com/query.php");
			/*HttpPost httppost = new HttpPost("http://www.weightmanager.site90.com/upload.php");
			
			// Add your data
			List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
			nameValuePairs.add(new BasicNameValuePair("image_id", ""+imageID));
			httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));*/
			
			HttpResponse response = httpclient.execute(httppost);
			
			progress = 45;
			
			String responseBody = EntityUtils.toString(response.getEntity());
			Log.w("UPLOAD RESPONSE", responseBody);	
			qid = (String) responseBody.subSequence(responseBody.indexOf("api_sig:"), responseBody.indexOf("timestamp"));
			qid = (String) qid.subSequence(9, qid.indexOf("<"));
			Log.w("QID", qid);
		} catch (Exception e){
			e.printStackTrace();
		}
		
		// Publish
		progress = 50;
		
		for (int i=0; i<100; i++){
			try {
				 Thread.sleep(200);
				 progress += 0.1;
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
		}

		boolean found = false;
		int attempts = 0;
		while (!found && attempts < 50){
		
			// Create a new HttpClient and Post Header
			HttpClient httpclient = new DefaultHttpClient();
			HttpPost httppost = new HttpPost("http://api.iqengines.com/v1.2/result/");

			try {

				TreeMap<String, String> fields = new TreeMap<String, String>();

				fields.put("time_stamp", now());
				fields.put("api_key", key);
				fields.put("qid", qid);

				// Add your data
				List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
				nameValuePairs.add(new BasicNameValuePair("time_stamp", fields.get("time_stamp")));
				nameValuePairs.add(new BasicNameValuePair("qid", qid));
				nameValuePairs.add(new BasicNameValuePair("api_key", key));
				nameValuePairs.add(new BasicNameValuePair("api_sig",
						buildSignature(fields)));
				httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

				// Execute HTTP Post Request
				HttpResponse response = httpclient.execute(httppost);
				String responseBody = EntityUtils.toString(response.getEntity());
				
				Log.w("IQ ENGINE", responseBody);
				if (responseBody.contains("<labels>")){
					description = responseBody.substring(responseBody.indexOf("<labels>")+8,responseBody.indexOf("</labels>"));
					Log.w("DESCRIPTION",description);
					found = true;
				}
				attempts++;

			} catch (ClientProtocolException e) {
				// TODO Auto-generated catch block
			} catch (IOException e) {
				// TODO Auto-generated catch block
			}
			
			if (!found) {
				for (int i=0; i<100; i++){
					try {
						 Thread.sleep(100);
						 progress += 0.05;
					} catch (InterruptedException e1) {
						e1.printStackTrace();
					}
				}
			}
		}
		return description;
   }
   
   
   private void uploadImage(Bitmap image) {
		
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		image.compress(CompressFormat.JPEG, 60, bos);
	    byte[] data = bos.toByteArray();
	    
	    Log.w("ARRAY","Length: "+data.length);
	    ArrayList<byte[]> chunks = new ArrayList<byte[]>();
	    for (int i=0; i<(data.length/512); i++){
	    	Log.w("ARRAY",i*512+"-"+((i*512)+512));
	    	//byte[] chunk = Arrays.copyOfRange(data, i*512, (i*512)+512);
	    	byte[] chunk = new byte[512];
	    	for (int j=0; j<512; j++){
	    		chunk[j] = data[(i*512)+j];
	    	} chunks.add(chunk);
	    }
	    Log.w("ARRAY",((data.length/512)*512)+"-"+data.length);
	    //byte[] chunk = Arrays.copyOfRange(data, (data.length/512)*512, data.length);
	    int offset = ((data.length/512)*512);
	    int remainder = data.length - ((data.length/512)*512);
	    byte[] chunk = new byte[remainder];
    	for (int j=0; j<remainder; j++){
    		chunk[j] = data[offset+j];
    	} chunks.add(chunk);
	    
	    float progress = 10;
	    float progressInterval = (float) (0.01 + (30 / chunks.size()));
	    int retry = 0;
	    
		FTPClient con1 = new FTPClient();
		try
		{
		    con1.connect("server30.000webhost.com");
		    if (con1.login("a6058737", "faBBalic0"))
		    //con1.connect("server17.000webhost.com");
		    //if (con1.login("a6316579", "faBBalic0"))
			//con1.connect("weightmanager.clanteam.com");
		    //if (con1.login("weightmanager_clanteam", "15911591"))
			//con1.connect("littleflare.com");
		    //if (con1.login("littlefl", "concertshurts"))
		    {
		    	Log.w("FTP","Buffer: "+con1.getBufferSize());
		    	con1.setFileType(FTPClient.BINARY_FILE_TYPE);
		    	con1.enterLocalPassiveMode(); // important!
			    Log.w("Upload","Size of data: "+data.length+" bytes");
		        ByteArrayInputStream in = new ByteArrayInputStream(chunks.get(0));
		        Log.w("CREATING", "creating file");
		        boolean result = con1.storeFile("/public_html/image.jpg", in);
		        in.close();
		        if (result) { 
		        	Log.w("CREATING", "succeeded");
		        	progress += progressInterval;
		        }
		    }
		}
		catch (Exception e)
		{
			Log.w("APPENDED", "0 login not succeeded!");
			retry++;
		    e.printStackTrace();
		}

		try
		{
			con1.logout();
			con1.disconnect();
		}
		catch (IOException e)
		{
			Log.w("APPENDED", "0 try not succeeded!");
			retry++;
		    e.printStackTrace();
		}
		

		for (int i=0; i<chunks.size() && retry < 5; i++){
			if (i!=0){ // ignore first
				
				FTPClient con = new FTPClient();
				try
				{
				    con.connect("server30.000webhost.com");
				    if (con.login("a6058737", "faBBalic0"))
				    //con.connect("server17.000webhost.com");
				    //if (con.login("a6316579", "faBBalic0"))
				    //con.connect("weightmanager.clanteam.com");
				    //if (con.login("weightmanager_clanteam", "15911591"))
					//con.connect("littleflare.com");
				    //if (con.login("littlefl", "concertshurts"))
				    {
				        con.enterLocalPassiveMode(); // important!
				        con.setFileType(FTPClient.BINARY_FILE_TYPE);
				        Log.w("APPENDED", "appending "+i+"...");
				        ByteArrayInputStream in = new ByteArrayInputStream(chunks.get(i));
				        boolean result = con.appendFile("/public_html/image.jpg", in);
				        in.close();
				        if (result){
				        	Log.w("APPENDED", i+" succeeded");
				        	progress += progressInterval;
				        }
				        else {
				        	Log.w("APPENDED", i+"appending not succeeded, trying again");
				        	i--;
				        	retry++;
				        }
				    }
				    else {
				    	Log.w("APPENDED", i+" login not succeeded, trying again");
				    	i--;
				    	retry++;
				    }
				}
				catch (Exception e)
				{
					Log.w("APPENDED", i+" try not succeeded, trying again");
					i--;
					retry++;
				    e.printStackTrace();
				}

				try
				{
				    con.logout();
				    con.disconnect();
				}
				catch (IOException e)
				{
				    e.printStackTrace();
				}			
				
			}
		}
	}
   
   /*
	 * PRIVATE METHODS
	 */
	/**
	 * Returns the current time stamp using the following formatting :
	 * "YYYYmmDDHHMMSS"
	 * 
	 * @return a non null {@link String}
	 */
	private String now() {
		Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
		String date_format = "yyyyMMddkkmmss";
		SimpleDateFormat sdf = new SimpleDateFormat(date_format);
		sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
		return sdf.format(c.getTime());
	}


	/**
	 * Computes the signature of the API call
	 * 
	 * @param fields
	 *            A non-<code>null</code> {@link TreeMap} that contains the
	 *            arguments of the request.
	 * @return A non-<code>null</code> {@link String} : The message
	 *         authentication code
	 */
	private String buildSignature(TreeMap<String, String> fields) {
		// join key value pairs together
		String result = null;
		String raw_string = "";
		Iterator<String> i = fields.keySet().iterator();
		while (i.hasNext()) {
			String tmpKey = i.next();
			String value = fields.get(tmpKey);
			if (tmpKey.equals("img")) {
				// if the argument is an image, only keep the name of the file
				File image = new File(value);
				raw_string += "img" + image.getName();
			} else if (tmpKey.startsWith("images")) {
				// if the argument is an image, only keep the name of the file
				File image = new File(value);
				raw_string += "images" + image.getName();
			} else {
				raw_string += tmpKey + value;
			}
		}
		try {
			String HMAC_SHA1_ALGORITHM = "HmacSHA1";
			byte[] secret_bytes = secret.getBytes();
			SecretKeySpec secret_key = new SecretKeySpec(secret_bytes,
					HMAC_SHA1_ALGORITHM);

			Mac m = Mac.getInstance("HmacSHA1");
			m.init(secret_key);
			byte[] signature_raw = m.doFinal(raw_string.getBytes());

			// Convert raw bytes to Hex
			byte[] signature_hex = new Hex().encode(signature_raw);

			// Covert array of Hex bytes to a String
			result = new String(signature_hex, "ISO-8859-1");
		} catch (NoSuchAlgorithmException e) {
			return "Exception : NoSuchAlgorithmException" + e.getMessage();
		} catch (InvalidKeyException e) {
			return "Exception : InvalidKeyException" + e.getMessage();
		} catch (UnsupportedEncodingException e) {
			return "Exception : UnsupportedEncodingException" + e.getMessage();
		}
		return result;
	}	
}
